﻿using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class NetworkManager : MonoBehaviour
{
    private TcpClient _tcpClient;
    private NetworkStream _networkStream;
    private CancellationTokenSource _cts;
    private bool _isConnected;
    private readonly string _serverAddress = "127.0.0.1";
    private readonly int _port = 12345;
    private readonly TimeSpan _reconnectDelay = TimeSpan.FromSeconds(5);

    private void Start()
    {
        _cts = new CancellationTokenSource();
        ConnectAsync().Forget(); // Fire and forget pattern for async methods
    }

    private async Task ConnectAsync()
    {
        if (_isConnected)
        {
            Debug.LogWarning("Already connected.");
            return;
        }

        while (!_cts.Token.IsCancellationRequested)
        {
            try
            {
                _tcpClient = new TcpClient();
                await _tcpClient.ConnectAsync(_serverAddress, _port);
                _networkStream = _tcpClient.GetStream();
                _isConnected = true;
                Debug.Log("Connected to server.");

                // Start listening for incoming data
                ListenForDataAsync().Forget();
                break;
            }
            catch (Exception ex)
            {
                Debug.LogError($"Connection failed: {ex.Message}");
                _isConnected = false;
                await Task.Delay(_reconnectDelay, _cts.Token); // Wait before retrying
            }
        }
    }

    private async Task ListenForDataAsync()
    {
        byte[] buffer = new byte[1024];
        while (_isConnected && !_cts.Token.IsCancellationRequested)
        {
            try
            {
                int bytesRead = await _networkStream.ReadAsync(buffer, 0, buffer.Length, _cts.Token);
                if (bytesRead > 0)
                {
                    string receivedMessage = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                    Debug.Log($"Received: {receivedMessage}");
                }
                else
                {
                    Debug.LogWarning("Disconnected from server.");
                    _isConnected = false;
                    await ConnectAsync(); // Attempt to reconnect
                }
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error while reading data: {ex.Message}");
                _isConnected = false;
                await ConnectAsync(); // Attempt to reconnect
            }
        }
    }

    public async Task SendMessageAsync(string message)
    {
        if (!_isConnected)
        {
            Debug.LogWarning("Not connected. Cannot send message.");
            return;
        }

        try
        {
            byte[] data = Encoding.UTF8.GetBytes(message);
            await _networkStream.WriteAsync(data, 0, data.Length, _cts.Token);
            Debug.Log("Message sent.");
        }
        catch (Exception ex)
        {
            Debug.LogError($"Error while sending message: {ex.Message}");
            _isConnected = false;
            await ConnectAsync(); // Attempt to reconnect
        }
    }

    private void OnDestroy()
    {
        _cts.Cancel();
        _networkStream?.Close();
        _tcpClient?.Close();
        _cts.Dispose(); // Dispose of the CancellationTokenSource
    }
}

// Extension method for fire-and-forget async calls
public static class TaskExtensions
{
    public static void Forget(this Task task)
    {
        // Optional: Log unobserved exceptions if necessary
        task.ConfigureAwait(false).GetAwaiter().GetResult();
    }
}
